fix(permissions): handle properly CanRead permission from server
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>
Fri, 23 May 2025 17:53:41 +0000 (19:53 +0200)
committerMatthieu Gallien <matthieu.gallien@nextcloud.com>
Mon, 2 Jun 2025 11:40:40 +0000 (13:40 +0200)
if Readable permissions is missing, do nto try to download the file

if a file was downlaoded but Readable permission is now missing, remove
it

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
src/common/remotepermissions.cpp
src/common/remotepermissions.h
src/gui/tray/activitydata.cpp
src/libsync/discovery.cpp
test/sharetestutils.cpp
test/syncenginetestutils.cpp
test/testfolderman.cpp
test/testpermissions.cpp
test/testremotediscovery.cpp
test/testsyncengine.cpp
test/testsyncmove.cpp

index 8e1f088e8c978e53d66fec51d50639e33f3aa6bf..fc75c954d5dcfc0438585c91a607baa2d6bb5041 100644 (file)
@@ -27,7 +27,7 @@ namespace OCC {
 
 Q_LOGGING_CATEGORY(lcRemotePermissions, "nextcloud.sync.remotepermissions", QtInfoMsg)
 
-static const char letters[] = " WDNVCKRSMm";
+static const char letters[] = " GWDNVCKRSMm";
 
 
 template <typename Char>
@@ -102,6 +102,7 @@ RemotePermissions RemotePermissions::fromServerString(const QString &value,
                                                       MountedPermissionAlgorithm algorithm,
                                                       const QMap<QString, QString> &otherProperties)
 {
+    qCInfo(lcRemotePermissions()) << "decoding" << value;
     return internalFromServerString(value, otherProperties, algorithm);
 }
 
@@ -109,6 +110,7 @@ RemotePermissions RemotePermissions::fromServerString(const QString &value,
                                                       MountedPermissionAlgorithm algorithm,
                                                       const QVariantMap &otherProperties)
 {
+    qCInfo(lcRemotePermissions()) << "decoding" << value;
     return internalFromServerString(value, otherProperties, algorithm);
 }
 
index a84c359d3b4468c9d60c4484c40b0e0a3644576f..1bb6e9e744a8f9d9d148562c0babf88942a57dfd 100644 (file)
@@ -41,18 +41,19 @@ private:
 
 public:
     enum Permissions {
-        CanWrite = 1,             // W
-        CanDelete = 2,            // D
-        CanRename = 3,            // N
-        CanMove = 4,              // V
-        CanAddFile = 5,           // C
-        CanAddSubDirectories = 6, // K
-        CanReshare = 7,           // R
+        CanRead = 1,              // G
+        CanWrite,                 // W
+        CanDelete,                // D
+        CanRename,                // N
+        CanMove,                  // V
+        CanAddFile,               // C
+        CanAddSubDirectories,     // K
+        CanReshare,               // R
         // Note: on the server, this means SharedWithMe, but in discoveryphase.cpp we also set
         // this permission when the server reports the any "share-types"
-        IsShared = 8,             // S
-        IsMounted = 9,            // M
-        IsMountedSub = 10,        // m (internal: set if the parent dir has IsMounted)
+        IsShared,                 // S
+        IsMounted,                // M
+        IsMountedSub,             // m (internal: set if the parent dir has IsMounted)
 
         // Note: when adding support for more permissions, we need to invalid the cache in the database.
         // (by setting forceRemoteDiscovery in SyncJournalDb::checkConnect)
index f0fb09b18b0fb00a19df766b5dd8efa52b27369b..df6b2509622bb822d5cfa3fda92de129a90936bd 100644 (file)
@@ -130,7 +130,6 @@ OCC::Activity Activity::fromActivityJson(const QJsonObject &json, const AccountP
             auto word = match.captured(1);
             word.remove(subjectRichParameterBracesRe);
 
-            Q_ASSERT(activity._subjectRichParameters.contains(word));
             displayString = displayString.replace(match.captured(1), activity._subjectRichParameters[word].value<Activity::RichSubjectParameter>().name);
         }
 
index a97585a1835505c9dcd8a31a5c78dbff68ac2bb7..5d2ab48e46d0a84563761ad81b8eb2eafc3b1511 100644 (file)
@@ -858,6 +858,9 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it
             item->_modtime = serverEntry.modtime;
             item->_size = sizeOnServer;
             item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
+        } else if (serverEntry.isValid() && !serverEntry.isDirectory && !serverEntry.remotePerm.isNull() && !serverEntry.remotePerm.hasPermission(RemotePermissions::CanRead)) {
+            item->_instruction = CSYNC_INSTRUCTION_REMOVE;
+            item->_direction = SyncFileItem::Down;
         } else if (dbEntry._remotePerm != serverEntry.remotePerm || dbEntry._fileId != serverEntry.fileId || metaDataSizeNeedsUpdateForE2EeFilePlaceholder) {
             if (metaDataSizeNeedsUpdateForE2EeFilePlaceholder) {
                 // we are updating placeholder sizes after migrating from older versions with VFS + E2EE implicit hydration not supported
@@ -921,6 +924,13 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it
         return;
     }
 
+    if (serverEntry.isValid() && !serverEntry.isDirectory && !serverEntry.remotePerm.isNull() && !serverEntry.remotePerm.hasPermission(RemotePermissions::CanRead)) {
+        item->_instruction = CSYNC_INSTRUCTION_IGNORE;
+        emit _discoveryData->itemDiscovered(item);
+
+        return;
+    }
+
     // Potential NEW/NEW conflict is handled in AnalyzeLocal
     if (localEntry.isValid()) {
         postProcessServerNew(item, path, localEntry, serverEntry, dbEntry);
index 797c89a8f25f720f0c7ab08df7979083c910bc78..1f9f5e2d2942d4206a46807a3cbb5563d9c78cbc 100644 (file)
@@ -153,6 +153,7 @@ void ShareTestHelper::setup()
 
     const auto fakeFileInfo = fakeFolder.remoteModifier().find(testFileName);
     QVERIFY(fakeFileInfo);
+    fakeFileInfo->permissions.setPermission(RemotePermissions::CanRead);
     fakeFileInfo->permissions.setPermission(RemotePermissions::CanReshare);
     QVERIFY(fakeFolder.syncOnce());
     QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
index 58fecc6e8e6cd3c21fe7685fbde61dc7b58c9d99..f05effdb436898b97cdcd5fe07bd53880290da82 100644 (file)
@@ -406,7 +406,7 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces
         xml.writeTextElement(davUri, QStringLiteral("getlastmodified"), stringDate);
         xml.writeTextElement(davUri, QStringLiteral("getcontentlength"), QString::number(fileInfo.size));
         xml.writeTextElement(davUri, QStringLiteral("getetag"), QStringLiteral("\"%1\"").arg(QString::fromLatin1(fileInfo.etag)));
-        xml.writeTextElement(ocUri, QStringLiteral("permissions"), !fileInfo.permissions.isNull() ? QString(fileInfo.permissions.toString()) : fileInfo.isShared ? QStringLiteral("SRDNVCKW") : QStringLiteral("RDNVCKW"));
+        xml.writeTextElement(ocUri, QStringLiteral("permissions"), !fileInfo.permissions.isNull() ? QString(fileInfo.permissions.toString()) : fileInfo.isShared ? QStringLiteral("GSRDNVCKW") : QStringLiteral("GRDNVCKW"));
         xml.writeTextElement(ocUri, QStringLiteral("share-permissions"), QString::number(static_cast<int>(OCC::SharePermissions(OCC::SharePermissionRead |
                                                                                                                                 OCC::SharePermissionUpdate |
                                                                                                                                 OCC::SharePermissionCreate |
index fa01cb51a869e0b641917b2c607218e9fd7205c9..bec0a7e65047627daed263a22c951179684ac4ea 100644 (file)
@@ -178,6 +178,7 @@ private slots:
         fakeFolder.remoteModifier().insert(firstSharePath, 100);
         const auto firstShare = fakeFolder.remoteModifier().find(firstSharePath);
         QVERIFY(firstShare);
+        firstShare->permissions.setPermission(OCC::RemotePermissions::CanRead);
         firstShare->permissions.setPermission(OCC::RemotePermissions::IsShared);
 
         fakeFolder.remoteModifier().mkdir("A/B");
@@ -185,6 +186,7 @@ private slots:
         fakeFolder.remoteModifier().insert(secondSharePath, 100);
         const auto secondShare = fakeFolder.remoteModifier().find(secondSharePath);
         QVERIFY(secondShare);
+        secondShare->permissions.setPermission(OCC::RemotePermissions::CanRead);
         secondShare->permissions.setPermission(OCC::RemotePermissions::IsShared);
 
         FolderMan *folderman = FolderMan::instance();
index 5de4c4dd636750c3712aa088f3123f5f9d7b4854..fb16bb1e494090fe63e3776dea731f7159278fdd 100644 (file)
@@ -125,11 +125,11 @@ private slots:
 
         //create some files
         auto insertIn = [&](const QString &dir) {
-            fakeFolder.remoteModifier().insert(dir + "normalFile_PERM_WVND_.data", 100 );
-            fakeFolder.remoteModifier().insert(dir + "cannotBeRemoved_PERM_WVN_.data", 101 );
-            fakeFolder.remoteModifier().insert(dir + "canBeRemoved_PERM_D_.data", 102 );
-            fakeFolder.remoteModifier().insert(dir + "cannotBeModified_PERM_DVN_.data", cannotBeModifiedSize , 'A');
-            fakeFolder.remoteModifier().insert(dir + "canBeModified_PERM_W_.data", canBeModifiedSize );
+            fakeFolder.remoteModifier().insert(dir + "normalFile_PERM_GWVND_.data", 100 );
+            fakeFolder.remoteModifier().insert(dir + "cannotBeRemoved_PERM_GWVN_.data", 101 );
+            fakeFolder.remoteModifier().insert(dir + "canBeRemoved_PERM_GD_.data", 102 );
+            fakeFolder.remoteModifier().insert(dir + "cannotBeModified_PERM_GDVN_.data", cannotBeModifiedSize , 'A');
+            fakeFolder.remoteModifier().insert(dir + "canBeModified_PERM_GW_.data", canBeModifiedSize );
         };
 
         //put them in some directories
@@ -139,7 +139,7 @@ private slots:
         insertIn("readonlyDirectory_PERM_M_/" );
         fakeFolder.remoteModifier().mkdir("readonlyDirectory_PERM_M_/subdir_PERM_CK_");
         fakeFolder.remoteModifier().mkdir("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_");
-        fakeFolder.remoteModifier().insert("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data", 100);
+        fakeFolder.remoteModifier().insert("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_GWVND_.data", 100);
         applyPermissionsFromName(fakeFolder.remoteModifier());
 
         QVERIFY(fakeFolder.syncOnce());
@@ -210,13 +210,13 @@ private slots:
 
         //1. remove the file than cannot be removed
         //  (they should be recovered)
-        fakeFolder.localModifier().remove("normalDirectory_PERM_CKDNV_/cannotBeRemoved_PERM_WVN_.data");
-        removeReadOnly("readonlyDirectory_PERM_M_/cannotBeRemoved_PERM_WVN_.data");
+        fakeFolder.localModifier().remove("normalDirectory_PERM_CKDNV_/cannotBeRemoved_PERM_GWVN_.data");
+        removeReadOnly("readonlyDirectory_PERM_M_/cannotBeRemoved_PERM_GWVN_.data");
 
         //2. remove the file that can be removed
         //  (they should properly be gone)
-        removeReadOnly("normalDirectory_PERM_CKDNV_/canBeRemoved_PERM_D_.data");
-        removeReadOnly("readonlyDirectory_PERM_M_/canBeRemoved_PERM_D_.data");
+        removeReadOnly("normalDirectory_PERM_CKDNV_/canBeRemoved_PERM_GD_.data");
+        removeReadOnly("readonlyDirectory_PERM_M_/canBeRemoved_PERM_GD_.data");
 
         //3. Edit the files that cannot be modified
         //  (they should be recovered, and a conflict shall be created)
@@ -225,19 +225,19 @@ private slots:
             QFile(fakeFolder.localPath() + file).setPermissions(QFile::WriteOwner | QFile::ReadOwner);
             fakeFolder.localModifier().appendByte(file);
         };
-        editReadOnly("normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_DVN_.data");
+        editReadOnly("normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_GDVN_.data");
 #if !defined Q_OS_WINDOWS
-        editReadOnly("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data");
+        editReadOnly("readonlyDirectory_PERM_M_/cannotBeModified_PERM_GDVN_.data");
 #endif
 
         //4. Edit other files
         //  (they should be uploaded)
-        fakeFolder.localModifier().appendByte("normalDirectory_PERM_CKDNV_/canBeModified_PERM_W_.data");
-        fakeFolder.localModifier().appendByte("readonlyDirectory_PERM_M_/canBeModified_PERM_W_.data");
+        fakeFolder.localModifier().appendByte("normalDirectory_PERM_CKDNV_/canBeModified_PERM_GW_.data");
+        fakeFolder.localModifier().appendByte("readonlyDirectory_PERM_M_/canBeModified_PERM_GW_.data");
 
         //5. Create a new file in a read write folder
         // (should be uploaded)
-        fakeFolder.localModifier().insert("normalDirectory_PERM_CKDNV_/newFile_PERM_WDNV_.data", 106 );
+        fakeFolder.localModifier().insert("normalDirectory_PERM_CKDNV_/newFile_PERM_GWDNV_.data", 106 );
         applyPermissionsFromName(fakeFolder.remoteModifier());
 
         //do the sync
@@ -247,26 +247,26 @@ private slots:
 
         //1.
         // File should be recovered
-        QVERIFY(currentLocalState.find("normalDirectory_PERM_CKDNV_/cannotBeRemoved_PERM_WVN_.data"));
+        QVERIFY(currentLocalState.find("normalDirectory_PERM_CKDNV_/cannotBeRemoved_PERM_GWVN_.data"));
 #if !defined Q_OS_WINDOWS
-        QCOMPARE(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data")->size, cannotBeModifiedSize);
+        QCOMPARE(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeModified_PERM_GDVN_.data")->size, cannotBeModifiedSize);
 #endif
-        QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeRemoved_PERM_WVN_.data"));
+        QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeRemoved_PERM_GWVN_.data"));
 
         //2.
         // File should be deleted
-        QVERIFY(!currentLocalState.find("normalDirectory_PERM_CKDNV_/canBeRemoved_PERM_D_.data"));
-        QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/canBeRemoved_PERM_D_.data"));
+        QVERIFY(!currentLocalState.find("normalDirectory_PERM_CKDNV_/canBeRemoved_PERM_GD_.data"));
+        QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/canBeRemoved_PERM_GD_.data"));
 
         //3.
         // File should be recovered
-        QCOMPARE(currentLocalState.find("normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_DVN_.data")->size, cannotBeModifiedSize);
+        QCOMPARE(currentLocalState.find("normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_GDVN_.data")->size, cannotBeModifiedSize);
         // and conflict created
-        auto c1 = findConflict(currentLocalState, "normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_DVN_.data");
+        auto c1 = findConflict(currentLocalState, "normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_GDVN_.data");
         QVERIFY(c1);
         QCOMPARE(c1->size, cannotBeModifiedSize + 1);
 #if !defined Q_OS_WINDOWS
-        auto c2 = findConflict(currentLocalState, "readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data");
+        auto c2 = findConflict(currentLocalState, "readonlyDirectory_PERM_M_/cannotBeModified_PERM_GDVN_.data");
         QVERIFY(c2);
         QCOMPARE(c2->size, cannotBeModifiedSize + 1);
 #endif
@@ -277,12 +277,12 @@ private slots:
 #endif
 
         //4. File should be updated, that's tested by assertLocalAndRemoteDir
-        QCOMPARE(currentLocalState.find("normalDirectory_PERM_CKDNV_/canBeModified_PERM_W_.data")->size, canBeModifiedSize + 1);
-        QCOMPARE(currentLocalState.find("readonlyDirectory_PERM_M_/canBeModified_PERM_W_.data")->size, canBeModifiedSize + 1);
+        QCOMPARE(currentLocalState.find("normalDirectory_PERM_CKDNV_/canBeModified_PERM_GW_.data")->size, canBeModifiedSize + 1);
+        QCOMPARE(currentLocalState.find("readonlyDirectory_PERM_M_/canBeModified_PERM_GW_.data")->size, canBeModifiedSize + 1);
 
         //5.
         // the file should be in the server and local
-        QVERIFY(currentLocalState.find("normalDirectory_PERM_CKDNV_/newFile_PERM_WDNV_.data"));
+        QVERIFY(currentLocalState.find("normalDirectory_PERM_CKDNV_/newFile_PERM_GWDNV_.data"));
 
         // Both side should still be the same
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
@@ -291,7 +291,7 @@ private slots:
 
         //6. Create a new file in a read only folder
         // (they should not be uploaded)
-        insertReadOnly("readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data", 105 );
+        insertReadOnly("readonlyDirectory_PERM_M_/newFile_PERM_GWDNV_.data", 105 );
 
         applyPermissionsFromName(fakeFolder.remoteModifier());
         // error: can't upload to readonly
@@ -302,8 +302,8 @@ private slots:
 
         //6.
         // The file should not exist on the remote, and not be there
-        QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data"));
-        QVERIFY(!fakeFolder.currentRemoteState().find("readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data"));
+        QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/newFile_PERM_GWDNV_.data"));
+        QVERIFY(!fakeFolder.currentRemoteState().find("readonlyDirectory_PERM_M_/newFile_PERM_GWDNV_.data"));
         // Both side should still be the same
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
 
@@ -316,7 +316,7 @@ private slots:
         QVERIFY(fakeFolder.syncOnce());
         assertCsyncJournalOk(fakeFolder.syncJournal());
         currentLocalState = fakeFolder.currentLocalState();
-        QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeRemoved_PERM_WVN_.data"));
+        QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeRemoved_PERM_GWVN_.data"));
         QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_"));
         // the subdirectory had delete permissions, but, it was within the recovered directory, so must also get recovered
         QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_"));
@@ -341,16 +341,16 @@ private slots:
         QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_"));
         // contents moved (had move permissions)
         QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_"));
-        QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data"));
+        QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_GWVND_.data"));
 
         // new still exist  (and is uploaded)
-        QVERIFY(currentLocalState.find("normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data"));
+        QVERIFY(currentLocalState.find("normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_/subsubdir_PERM_CKDNV_/normalFile_PERM_GWVND_.data"));
 
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
 
         // restore for further tests
         fakeFolder.remoteModifier().mkdir("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_");
-        fakeFolder.remoteModifier().insert("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data");
+        fakeFolder.remoteModifier().insert("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_GWVND_.data");
         applyPermissionsFromName(fakeFolder.remoteModifier());
         QVERIFY(fakeFolder.syncOnce());
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
@@ -364,7 +364,7 @@ private slots:
         QVERIFY(fakeFolder.syncOnce());
         assertCsyncJournalOk(fakeFolder.syncJournal());
 
-        QVERIFY(fakeFolder.currentLocalState().find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data" ));
+        QVERIFY(fakeFolder.currentLocalState().find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_GWVND_.data" ));
 
         //1. rename a directory in a read only folder
         //Missing directory should be restored
@@ -382,9 +382,9 @@ private slots:
         // old name restored
         QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_" ));
         // including contents
-        QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data" ));
+        QVERIFY(currentLocalState.find("readonlyDirectory_PERM_M_/subdir_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_GWVND_.data" ));
         // new no longer exists
-        QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/newname_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data" ));
+        QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/newname_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_GWVND_.data" ));
         // but is not on server: should have been locally removed
         QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/newname_PERM_CK_"));
 
@@ -394,7 +394,7 @@ private slots:
         // but still on the server: the rename causing an error meant the deletes didn't execute
         QVERIFY(fakeFolder.currentRemoteState().find("normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_"));
         // new no longer exists
-        QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/moved_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_WVND_.data" ));
+        QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/moved_PERM_CK_/subsubdir_PERM_CKDNV_/normalFile_PERM_GWVND_.data" ));
         // should have been cleaned up as invalid item inside read-only folder
         QVERIFY(!currentLocalState.find("readonlyDirectory_PERM_M_/moved_PERM_CK_"));
         fakeFolder.remoteModifier().remove("normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_");
@@ -405,20 +405,20 @@ private slots:
         //######################################################################
         qInfo( "multiple restores of a file create different conflict files" );
 
-        fakeFolder.remoteModifier().insert("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data");
+        fakeFolder.remoteModifier().insert("readonlyDirectory_PERM_M_/cannotBeModified_PERM_GDVN_.data");
         applyPermissionsFromName(fakeFolder.remoteModifier());
         QVERIFY(fakeFolder.syncOnce());
 
-        editReadOnly("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data");
-        fakeFolder.localModifier().setContents("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data", 's');
+        editReadOnly("readonlyDirectory_PERM_M_/cannotBeModified_PERM_GDVN_.data");
+        fakeFolder.localModifier().setContents("readonlyDirectory_PERM_M_/cannotBeModified_PERM_GDVN_.data", 's');
         //do the sync
         applyPermissionsFromName(fakeFolder.remoteModifier());
         QVERIFY(fakeFolder.syncOnce());
         assertCsyncJournalOk(fakeFolder.syncJournal());
 
         QThread::sleep(1); // make sure changes have different mtime
-        editReadOnly("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data");
-        fakeFolder.localModifier().setContents("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data", 'd');
+        editReadOnly("readonlyDirectory_PERM_M_/cannotBeModified_PERM_GDVN_.data");
+        fakeFolder.localModifier().setContents("readonlyDirectory_PERM_M_/cannotBeModified_PERM_GDVN_.data", 'd');
 
         //do the sync
         applyPermissionsFromName(fakeFolder.remoteModifier());
@@ -428,7 +428,7 @@ private slots:
         // there should be two conflict files
         currentLocalState = fakeFolder.currentLocalState();
         int count = 0;
-        while (auto i = findConflict(currentLocalState, "readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data")) {
+        while (auto i = findConflict(currentLocalState, "readonlyDirectory_PERM_M_/cannotBeModified_PERM_GDVN_.data")) {
             QVERIFY((i->contentChar == 's') || (i->contentChar == 'd'));
             removeReadOnly(i->path());
             currentLocalState = fakeFolder.currentLocalState();
@@ -487,10 +487,10 @@ private slots:
         rm.insert("zallowed/sub/file");
         rm.insert("zallowed/sub2/file");
 
-        setAllPerm(rm.find("norename"), RemotePermissions::fromServerString("WDVCK"));
-        setAllPerm(rm.find("nomove"), RemotePermissions::fromServerString("WDNCK"));
-        setAllPerm(rm.find("nocreatefile"), RemotePermissions::fromServerString("WDNVK"));
-        setAllPerm(rm.find("nocreatedir"), RemotePermissions::fromServerString("WDNVC"));
+        setAllPerm(rm.find("norename"), RemotePermissions::fromServerString("GWDVCK"));
+        setAllPerm(rm.find("nomove"), RemotePermissions::fromServerString("GWDNCK"));
+        setAllPerm(rm.find("nocreatefile"), RemotePermissions::fromServerString("GWDNVK"));
+        setAllPerm(rm.find("nocreatedir"), RemotePermissions::fromServerString("GWDNVC"));
 
         QVERIFY(fakeFolder.syncOnce());
 
@@ -715,7 +715,7 @@ private slots:
         remote.mkdir("testFolder/newSubFolder");
         remote.create("testFolder/testFile", 12, '9');
         remote.create("testFolder/testReadOnlyFile", 13, '8');
-        remote.find("testFolder/testReadOnlyFile")->permissions = RemotePermissions::fromServerString("m");
+        remote.find("testFolder/testReadOnlyFile")->permissions = RemotePermissions::fromServerString("mG");
 
         QVERIFY(fakeFolder.syncOnce());
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
@@ -746,7 +746,7 @@ private slots:
 
         remote.find("readOnlyFolder")->permissions = RemotePermissions::fromServerString("M");
         remote.find("readOnlyFolder/test")->permissions = RemotePermissions::fromServerString("m");
-        remote.find("readOnlyFolder/readOnlyFile.txt")->permissions = RemotePermissions::fromServerString("m");
+        remote.find("readOnlyFolder/readOnlyFile.txt")->permissions = RemotePermissions::fromServerString("mG");
 
         QVERIFY(fakeFolder.syncOnce());
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
@@ -776,7 +776,7 @@ private slots:
 
         remote.find("readOnlyFolder")->permissions = RemotePermissions::fromServerString("M");
         remote.find("readOnlyFolder/test")->permissions = RemotePermissions::fromServerString("m");
-        remote.find("readOnlyFolder/readOnlyFile.txt")->permissions = RemotePermissions::fromServerString("m");
+        remote.find("readOnlyFolder/readOnlyFile.txt")->permissions = RemotePermissions::fromServerString("mG");
 
         QVERIFY(fakeFolder.syncOnce());
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
@@ -810,7 +810,7 @@ private slots:
         remote.find("readOnlyFolder")->permissions = RemotePermissions::fromServerString("M");
         remote.find("readOnlyFolder/child")->permissions = RemotePermissions::fromServerString("m");
         remote.find("readOnlyFolder/test")->permissions = RemotePermissions::fromServerString("m");
-        remote.find("readOnlyFolder/readOnlyFile.txt")->permissions = RemotePermissions::fromServerString("m");
+        remote.find("readOnlyFolder/readOnlyFile.txt")->permissions = RemotePermissions::fromServerString("mG");
 
         QVERIFY(fakeFolder.syncOnce());
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
@@ -843,13 +843,13 @@ private slots:
 
         remote.find("readOnlyFolder")->permissions = RemotePermissions::fromServerString("M");
         remote.find("readOnlyFolder/test")->permissions = RemotePermissions::fromServerString("m");
-        remote.find("readOnlyFolder/readOnlyFile.txt")->permissions = RemotePermissions::fromServerString("m");
+        remote.find("readOnlyFolder/readOnlyFile.txt")->permissions = RemotePermissions::fromServerString("mG");
 
         QVERIFY(fakeFolder.syncOnce());
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
 
         remote.insert("readOnlyFolder/test/newFile.txt");
-        remote.find("readOnlyFolder/test/newFile.txt")->permissions = RemotePermissions::fromServerString("m");
+        remote.find("readOnlyFolder/test/newFile.txt")->permissions = RemotePermissions::fromServerString("mG");
         remote.mkdir("readOnlyFolder/test/newFolder");
         remote.find("readOnlyFolder/test/newFolder")->permissions = RemotePermissions::fromServerString("m");
         remote.appendByte("readOnlyFolder/readOnlyFile.txt");
@@ -867,6 +867,69 @@ private slots:
         QVERIFY(ensureReadOnlyItem("/readOnlyFolder/test/newFile.txt"));
         QVERIFY(ensureReadOnlyItem("/readOnlyFolder/newFolder"));
     }
+
+    void testForbiddenDownload()
+    {
+        FakeFolder fakeFolder{FileInfo{}};
+        QObject parent;
+
+        fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) -> QNetworkReply * {
+            Q_UNUSED(outgoingData)
+
+            if (op == QNetworkAccessManager::GetOperation) {
+                return new FakeErrorReply(op, request, &parent, 403, "Access to this shared resource has been denied because its download permission is disabled.");
+            }
+
+            return nullptr;
+        });
+
+        fakeFolder.remoteModifier().insert("file");
+
+        setAllPerm(fakeFolder.remoteModifier().find("file"), RemotePermissions::fromServerString("DNVRS"));
+
+        // also hook into discovery!!
+        SyncFileItemVector discovery;
+        connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToPropagate, this, [&discovery](auto v) { discovery = v; });
+        ItemCompletedSpy completeSpy(fakeFolder);
+        QVERIFY(fakeFolder.syncOnce());
+
+        QVERIFY(itemInstruction(completeSpy, "file", CSYNC_INSTRUCTION_IGNORE));
+        QVERIFY(discoveryInstruction(discovery, "file", CSYNC_INSTRUCTION_IGNORE));
+    }
+
+    void testExistingFileBecomeForbiddenDownload()
+    {
+        FakeFolder fakeFolder{FileInfo{}};
+        QObject parent;
+
+        fakeFolder.remoteModifier().insert("file");
+        auto fileInfo = fakeFolder.remoteModifier().find("file");
+        Q_ASSERT(fileInfo);
+        fileInfo->isShared = true;
+
+        QVERIFY(fakeFolder.syncOnce());
+
+        fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) -> QNetworkReply * {
+            Q_UNUSED(outgoingData)
+
+            if (op == QNetworkAccessManager::GetOperation) {
+                return new FakeErrorReply(op, request, &parent, 403, "Access to this shared resource has been denied because its download permission is disabled.");
+            }
+
+            return nullptr;
+        });
+
+        setAllPerm(fileInfo, RemotePermissions::fromServerString("DNVRS"));
+
+        // also hook into discovery!!
+        SyncFileItemVector discovery;
+        connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToPropagate, this, [&discovery](auto v) { discovery = v; });
+        ItemCompletedSpy completeSpy(fakeFolder);
+        QVERIFY(fakeFolder.syncOnce());
+
+        QVERIFY(itemInstruction(completeSpy, "file", CSYNC_INSTRUCTION_REMOVE));
+        QVERIFY(discoveryInstruction(discovery, "file", CSYNC_INSTRUCTION_REMOVE));
+    }
 };
 
 QTEST_GUILESS_MAIN(TestPermissions)
index a688b8d92cf76e3971c9d7831c7a3fa0ed0d7424..5eef1e7b9e5b7b1af5e6a98822ad4b40b9b943da 100644 (file)
@@ -27,7 +27,7 @@ struct MissingPermissionsPropfindReply : FakePropfindReply {
                                const QNetworkRequest &request, QObject *parent)
         : FakePropfindReply(remoteRootFileInfo, op, request, parent) {
         // If the propfind contains a single file without permissions, this is a server error
-        const char toRemove[] = "<oc:permissions>RDNVCKW</oc:permissions>";
+        const char toRemove[] = "<oc:permissions>GRDNVCKW</oc:permissions>";
         auto pos = payload.indexOf(toRemove, payload.size()/2);
         QVERIFY(pos > 0);
         payload.remove(pos, sizeof(toRemove) - 1);
index cd10e51bb109101bf78395ac7c730edb30bff0e5..5a53904a55ab60bf0d5aa88e134674f307f9b267 100644 (file)
@@ -2255,9 +2255,9 @@ private slots:
         fakeFolder.remoteModifier().insert("file3");
 
         fakeFolder.remoteModifier().find("folder")->permissions = RemotePermissions::fromServerString("DNVS");
-        fakeFolder.remoteModifier().find("folder/file1.lnk")->permissions = RemotePermissions::fromServerString("S");
-        fakeFolder.remoteModifier().find("folder/file2.lnk")->permissions = RemotePermissions::fromServerString("S");
-        fakeFolder.remoteModifier().find("folder/file3.lnk")->permissions = RemotePermissions::fromServerString("S");
+        fakeFolder.remoteModifier().find("folder/file1.lnk")->permissions = RemotePermissions::fromServerString("SG");
+        fakeFolder.remoteModifier().find("folder/file2.lnk")->permissions = RemotePermissions::fromServerString("SG");
+        fakeFolder.remoteModifier().find("folder/file3.lnk")->permissions = RemotePermissions::fromServerString("SG");
 
         fakeFolder.remoteModifier().find("abcdefabcdefabcdefabcdefabcdefabcd")->permissions = RemotePermissions::fromServerString("DNVS");
         fakeFolder.remoteModifier().find("abcdefabcdefabcdefabcdefabcdefabcd/abcdef abcdef abcdef a")->permissions = RemotePermissions::fromServerString("DNVS");
@@ -2267,7 +2267,7 @@ private slots:
         fakeFolder.remoteModifier().find("abcdefabcdefabcdefabcdefabcdefabcd/abcdef abcdef abcdef a/abcdef abcdef/abcdef acbdef abcd/123abcdefabcdef1/123123abcdef123 abcdef1")->permissions = RemotePermissions::fromServerString("DNVS");
         fakeFolder.remoteModifier().find("abcdefabcdefabcdefabcdefabcdefabcd/abcdef abcdef abcdef a/abcdef abcdef/abcdef acbdef abcd/123abcdefabcdef1/123123abcdef123 abcdef1/12abcabc")->permissions = RemotePermissions::fromServerString("DNVS");
         fakeFolder.remoteModifier().find("abcdefabcdefabcdefabcdefabcdefabcd/abcdef abcdef abcdef a/abcdef abcdef/abcdef acbdef abcd/123abcdefabcdef1/123123abcdef123 abcdef1/12abcabc/12abcabd")->permissions = RemotePermissions::fromServerString("DNVS");
-        fakeFolder.remoteModifier().find("abcdefabcdefabcdefabcdefabcdefabcd/abcdef abcdef abcdef a/abcdef abcdef/abcdef acbdef abcd/123abcdefabcdef1/123123abcdef123 abcdef1/12abcabc/12abcabd/this is a long long long long long long long long long long long long long long long long l.docx - Sh.lnk")->permissions = RemotePermissions::fromServerString("S");
+        fakeFolder.remoteModifier().find("abcdefabcdefabcdefabcdefabcdefabcd/abcdef abcdef abcdef a/abcdef abcdef/abcdef acbdef abcd/123abcdefabcdef1/123123abcdef123 abcdef1/12abcabc/12abcabd/this is a long long long long long long long long long long long long long long long long l.docx - Sh.lnk")->permissions = RemotePermissions::fromServerString("SG");
 
         QVERIFY(fakeFolder.syncOnce());
     }
index 52d0051b69431904cf53d90020a22bde2993a50b..04d9456e053cfe4f45148ae60aea5cbe69186eb1 100644 (file)
@@ -326,7 +326,7 @@ private slots:
         fakeFolder.remoteModifier().mkdir("external-storage");
         auto externalStorage = fakeFolder.remoteModifier().find("external-storage");
         externalStorage->extraDavProperties = "<nc:is-mount-root>true</nc:is-mount-root>";
-        setAllPerm(externalStorage, RemotePermissions::fromServerString("WDNVCKRM"));
+        setAllPerm(externalStorage, RemotePermissions::fromServerString("WDNVCKRMG"));
         QVERIFY(fakeFolder.syncOnce());
 
         OperationCounter operationCounter;
@@ -1159,7 +1159,7 @@ private slots:
         fakeFolder.remoteModifier().mkdir("FolA");
         auto groupFolderRoot = fakeFolder.remoteModifier().find("FolA");
         groupFolderRoot->extraDavProperties = "<nc:is-mount-root>true</nc:is-mount-root>";
-        setAllPerm(groupFolderRoot, RemotePermissions::fromServerString("WDNVCKRM"));
+        setAllPerm(groupFolderRoot, RemotePermissions::fromServerString("WDNVCKRMG"));
         fakeFolder.remoteModifier().mkdir("FolA/FolB");
         fakeFolder.remoteModifier().mkdir("FolA/FolB/FolC");
         fakeFolder.remoteModifier().mkdir("FolA/FolB/FolC/FolD");
@@ -1196,7 +1196,7 @@ private slots:
         fakeFolder.remoteModifier().mkdir("FolA");
         auto groupFolderRoot = fakeFolder.remoteModifier().find("FolA");
         groupFolderRoot->extraDavProperties = "<nc:is-mount-root>true</nc:is-mount-root>";
-        setAllPerm(groupFolderRoot, RemotePermissions::fromServerString("WDNVCKRM"));
+        setAllPerm(groupFolderRoot, RemotePermissions::fromServerString("WDNVCKRMG"));
         fakeFolder.remoteModifier().mkdir("FolA/FolB");
         fakeFolder.remoteModifier().mkdir("FolA/FolB/FolC");
         fakeFolder.remoteModifier().mkdir("FolA/FolB/FolC/FolD");